/*
 *
 *  *
 *  *   Copyright 2020-2021 Luter.me
 *  *
 *  *   Licensed under the Apache License, Version 2.0 (the "License");
 *  *   you may not use this file except in compliance with the License.
 *  *   You may obtain a copy of the License at
 *  *
 *  *   http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *   Unless required by applicable law or agreed to in writing, software
 *  *   distributed under the License is distributed on an "AS IS" BASIS,
 *  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *   See the License for the specific language governing permissions and
 *  *   limitations under the License.
 *  *
 *
 */

package com.luter.heimdall.core.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * The type Str utils.
 *
 * @author luter
 */
public abstract class StrUtils {

    /**
     * 逗号
     */
    public static final String COLON = ":";
    /**
     * 空字符串
     */
    public static final String EMPTY_STRING = "";
    /**
     * 一个空格
     */
    public static final String ONE_BLANK_SPACE_STRING = " ";
    /**
     * JWT format has 3 point
     */
    private static final int COUNT_3 = 3;

    /**
     * Determine whether it is a base64 string
     */
    private static final Pattern BASE64_PATTERN =
            Pattern.compile("^([A-Za-z0-9+/_-]+)(=*)$");

    /**
     * Instantiates a new Str utils.
     */
    private StrUtils() {
    }

    /**
     * Is empty boolean.
     *
     * @param charSequence the char sequence
     * @return the boolean
     */
    public static boolean isEmpty(CharSequence charSequence) {
        return charSequence == null || charSequence.toString().isEmpty();
    }

    /**
     * Is not empty boolean.
     *
     * @param charSequence the char sequence
     * @return the boolean
     */
    public static boolean isNotEmpty(CharSequence charSequence) {
        return !StrUtils.isEmpty(charSequence);
    }

    /**
     * Is empty boolean.
     *
     * @param str the str
     * @return the boolean
     */
    public static boolean isEmpty(String str) {
        return str == null || str.length() == 0;
    }

    /**
     * Is not empty boolean.
     *
     * @param str the str
     * @return the boolean
     */
    public static boolean isNotEmpty(String str) {
        return !StrUtils.isEmpty(str);
    }

    /**
     * Is blank boolean.
     *
     * @param str the str
     * @return the boolean
     */
    public static boolean isBlank(String str) {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ((!Character.isWhitespace(str.charAt(i)))) {
                return false;
            }
        }
        return true;
    }

    /**
     * Is not blank boolean.
     *
     * @param str the str
     * @return the boolean
     */
    public static boolean isNotBlank(String str) {
        return !StrUtils.isBlank(str);
    }

    /**
     * Cast list.
     *
     * @param <T>   the type parameter
     * @param obj   the obj
     * @param clazz the clazz
     * @return the list
     */
    public static <T> List<T> castList(Object obj, Class<T> clazz) {
        List<T> result = new ArrayList<>();
        if (obj instanceof List<?>) {
            for (Object o : (List<?>) obj) {
                result.add(clazz.cast(o));
            }
            return result;
        }
        return null;
    }

    /**
     * Clean string.
     *
     * @param in the in
     * @return the string
     */
    public static String clean(String in) {
        String out = in;

        if (in != null) {
            out = in.trim();
            if (out.equals(EMPTY_STRING)) {
                out = null;
            }
        }

        return out;
    }

    /**
     * Has length boolean.
     *
     * @param str the str
     * @return the boolean
     */
    public static boolean hasLength(String str) {
        return (str != null && str.length() > 0);
    }

    /**
     * Has text boolean.
     *
     * @param str the str
     * @return the boolean
     */
    public static boolean hasText(String str) {
        if (!hasLength(str)) {
            return false;
        }
        int strLen = str.length();
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 下划线转驼峰
     * user_name  ---->  userName
     * userName   --->  userName
     *
     * @param underlineStr 带有下划线的字符串
     * @return 驼峰字符串 string
     */
    public static String toCamelCase(String underlineStr) {
        if (underlineStr == null) {
            return null;
        }
        // 分成数组
        char[] charArray = underlineStr.toCharArray();
        // 判断上次循环的字符是否是"_"
        boolean underlineBefore = false;
        StringBuilder buffer = new StringBuilder();
        for (int i = 0, l = charArray.length; i < l; i++) {
            // 判断当前字符是否是"_",如果跳出本次循环
            if (charArray[i] == 95) {
                underlineBefore = true;
            } else if (underlineBefore) {
                // 如果为true，代表上次的字符是"_",当前字符需要转成大写
                buffer.append(charArray[i] -= 32);
                underlineBefore = false;
            } else {
                // 不是"_"后的字符就直接追加
                buffer.append(charArray[i]);
            }
        }
        return buffer.toString();
    }

    /**
     * 驼峰转 下划线
     * userName  ---->  user_name
     * user_name  ---->  user_name
     *
     * @param camelCaseStr 驼峰字符串
     * @return 带下滑线的String string
     */
    public static String toUnderlineCase(String camelCaseStr) {
        if (camelCaseStr == null) {
            return null;
        }
        // 将驼峰字符串转换成数组
        char[] charArray = camelCaseStr.toCharArray();
        StringBuilder buffer = new StringBuilder();
        //处理字符串
        for (int i = 0, l = charArray.length; i < l; i++) {
            if (charArray[i] >= 65 && charArray[i] <= 90) {
                buffer.append("_").append(charArray[i] += 32);
            } else {
                buffer.append(charArray[i]);
            }
        }
        return buffer.toString();
    }

    /**
     * Count matches int.
     *
     * @param str the str
     * @param sub the sub
     * @return the int
     */
    public static int countMatches(CharSequence str, CharSequence sub) {
        if (!isEmpty(str) && !isEmpty(sub)) {
            int count = 0;

            for (int idx = 0; (idx = indexOf(str, sub, idx)) != -1; idx += sub.length()) {
                ++count;
            }

            return count;
        } else {
            return 0;
        }
    }

    /**
     * Index of int.
     *
     * @param cs         the cs
     * @param searchChar the search char
     * @param start      the start
     * @return the int
     */
    public static int indexOf(CharSequence cs, CharSequence searchChar, int start) {
        if (cs instanceof String) {
            return ((String) cs).indexOf(searchChar.toString(), start);
        } else if (cs instanceof StringBuilder) {
            return ((StringBuilder) cs).indexOf(searchChar.toString(), start);
        } else {
            return cs instanceof StringBuffer ? ((StringBuffer) cs).indexOf(searchChar.toString(), start) : cs.toString().indexOf(searchChar.toString(), start);
        }
    }

    /**
     * Count occurrence int.
     *
     * @param word   the word
     * @param target the target
     * @return the int
     */
    public static int countOccurrence(String word, String target) {
        return word.length() - word.replace(target, "").length();
    }

    /**
     * Count long.
     *
     * @param word   the word
     * @param target the target
     * @return the long
     */
    public static long count(String word, char target) {
//        long count2 = word.codePoints().filter(ch -> ch == target).count();
        return word.chars().filter(ch -> ch == target).count();
    }

    /**
     * Count occurrence int.
     *
     * @param word      the word
     * @param character the character
     * @return the int
     */
    private static int countOccurrence(String word, char character) {
        int count = 0;
        for (int i = 0; i < word.length(); i++) {
            if (word.charAt(i) == character) {
                count++;
            }
        }
        return count;
    }

    /**
     * 不是一个合法的 jwt token 字符串
     *
     * @param jwt the jwt
     * @return the boolean
     */
    public static boolean isNotValidJwtToken(String jwt) {
        if (StrUtils.isBlank(jwt)) {
            return true;
        }
        // base64url_encode(Header) + '.' + base64url_encode(Claims) + '.' + base64url_encode(Signature)
        String[] jwtArr = jwt.split("\\.");
        if (jwtArr.length != COUNT_3) {
            return true;
        }
        for (String jwtTmp : jwtArr) {
            if (!BASE64_PATTERN.matcher(jwtTmp).matches()) {
                return true;
            }
        }
        return false;
    }
}
